/**
 * This file is a part of qloudgen-sec. 
 * You can redistribute qloudgen-sec and/or modify it under the terms of the Lesser GNU General Public License version 3. 
 * qloudgen-sec is distributed WITHOUT ANY WARRANTY. 
 *
 * See the Lesser GNU General Public License for more details at http://www.gnu.org/licenses/. 
 **/
package org.qloudgen.sec.cas.client;

import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.ServletContext;
import javax.servlet.jsp.PageContext;

import org.pac4j.cas.profile.CasProfile;
import org.pac4j.cas.profile.CasProxyProfile;

import org.apache.shiro.web.env.IniWebEnvironment;
import org.apache.shiro.web.util.WebUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.PrincipalCollection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.qloudgen.sec.util.ExceptionUtil;
import org.qloudgen.sec.shiro.permission.DroolsPermission;

/**
 * <p>Title: Web Tools Implementation</p>
 * <p>Description: Wrappered shiro web enviroment implementation to.</p>
 * <p>Copyright: qloudgen/sec 2014</p>
 * @author <a href="mailto:hyq.dd.sun@gmail.com">glue.3M</a>
 */
@SuppressWarnings( { "serial" , "unchecked" } )
public final class WebUtil {
	/**
	 * Log output.
	 */
	private final static Logger logger = LoggerFactory.getLogger( WebUtil.class );
	
	/**
	 * Returns current shiro bean from servlet request.
	 *
	 * @param request
	 * @param clazz
	 * @param name
	 * @return <T>
	 */
	public static <T> T getBean(final ServletRequest request , final Class<T> clazz , final String name) {
		return getBean( request.getServletContext() , clazz , name );
	}
	
	/**
	 * Returns current shiro bean from jsp page.
	 *
	 * @param page
	 * @param clazz
	 * @param name
	 * @return <T>
	 */
	public static <T> T getBean(final PageContext page , final Class<T> clazz , final String name) {
		return getBean( page.getServletContext() , clazz , name );
	}
	
	/**
	 * Returns current shiro bean from servlet context.
	 *
	 * @param context
	 * @param clazz
	 * @param name
	 * @return <T>
	 */
	public static <T> T getBean(final ServletContext context , final Class<T> clazz , final String name) {
		//
		IniWebEnvironment env = ( IniWebEnvironment )WebUtils.getRequiredWebEnvironment( context );
		// Check
		if ( env == null ) {
			logger.warn( "Current shiro web enviroment is null..." );
			return null;
		}
		//
		return env.getObject( name , clazz );
	}
	
	/**
	 * Returns current user profile.
	 *
	 * @return CasProfile
	 */
	private static CasProfile getUserProfile() {
		/**
		 * First we do need get current authentication user principals by shiro.
		 * Shiro filter will convert subject from session model to thread model.
		 * 0)ShiroFilter will control all filters.
		 * 1)ShiroFilter will resolve principals from session.
		 * 2)ShiroFilter will bind subject to thread context.
		 */
		PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
		logger.trace( "Current principals is...{}" , principals );
		//
		if ( principals == null ) {
			return null;
		}
		
		/**
		 * Second we do need get current authentication user profile by pac4j.
		 */
		CasProfile profile = principals.oneByType( CasProfile.class );
		logger.trace( "Current profile is...{}" , profile );
		
		//
		return profile;
	}
	
	/**
	 * Returns current user id.
	 *
	 * @return String
	 */
	public static String getUserId() {
		CasProfile profile = getUserProfile();
		return ( profile == null ? null : profile.getId() );
	}
	
	/**
	 * Returns current user attributes.
	 *
	 * @return Map<String,Object>
	 */
	public static Map<String,Object> getUserAttributes() {
		CasProfile profile = getUserProfile();
		return ( profile == null ? null : profile.getAttributes() );
	}

	/**
	 * Returns current user proxy ticket which reference to current user proxy granting ticket.
	 * <p>1)After receieve proxy granting ticket.</p>
	 * <p>2)After cas authentication.</p>
	 * <p>3)After proxy granting ticket set into cas attribute principal.</p>
	 *
	 * @return String
	 */
	public static String getUserProxyTicket(final String service) {
		CasProfile profile = getUserProfile();
		//
		if ( profile != null ) {
			if ( profile instanceof CasProxyProfile ) {
				return ( ( CasProxyProfile )profile ).getProxyTicketFor( service );
			}
		}
		//
		return null;
	}

	/**
	 * Authorization.
	 *
	 * @param name
	 * @param facts
	 */
	public static boolean isPermitted(final String name , final Object[] facts) {
		//
		try {
			//
			logger.info( "Starting authorization..." );
			/**
			 * Do create subject..
			 */
			Subject subject = SecurityUtils.getSubject();
			if ( ! subject.isAuthenticated() ) {
				return false;
			}
			/**
			 * Do really authorization.
			 */
			boolean result = subject.isPermitted( new DroolsPermission( name , facts ) );
			//
			logger.info( "Started authorization...{}" , result );
			//
			return result;
		}
		catch (Throwable t) {
			logger.error( "Current authorization is fail...\n{}" , ExceptionUtil.get( t ) );
			return false;
		}
	}
}